package spindles.api.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import spindles.api.util.ApplicationException;
import spindles.api.util.Config;

import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.TransactionConfig;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.StoreConfig;
import com.sleepycat.persist.model.AnnotationModel;
import com.sleepycat.persist.model.EntityModel;

public class DB {
	
	final Logger log = LoggerFactory.getLogger(DB.class);
	
	private Environment env;
	private EntityStore store;
	
	private TransactionConfig txnConfig;
	
	private CursorConfig cursorConfig;
	
	private static DB db = new DB();
	
	protected DB() {
		init();
	}
	
	private void init(){
		initEnv(); 
		initEntityStore();
		
		//Use uncommitted reads 
		txnConfig = new TransactionConfig();
	    txnConfig.setReadUncommitted(true);
	    txnConfig.setSync(true);
	    cursorConfig = new CursorConfig();
		cursorConfig.setReadUncommitted(true);
	}
	
	
	public static TransactionConfig getTxnConfig(){
		return db.txnConfig;
	}
	
	public static CursorConfig getCursorConfig(){
		return db.cursorConfig;
	}

	public static void runCmd(DBCommand cmd){
		cmd.run();
	}
	
	public static void runCmd(Closeable c){
		c.run();
	}
	
	public static void runTxn(Closeable c){
		c.runTxn();
	}
	
	public static Transaction beginTransaction(){		
		try {
			return db.env.beginTransaction(null, db.txnConfig);
		} catch (DatabaseException e) {
			throw new ApplicationException(e);
		}
	}
	
	public static void commit(Transaction txn){
		try {
			txn.commit();
		} catch (DatabaseException e) {
			throw new ApplicationException(e);
		}
	}
	
	public static EntityStore getStore(){
		return db.store;
	}
	
	private void initEntityStore(){
		runCmd(new DBCommand(){
			public void execute() throws DatabaseException {			
				
				StoreConfig config = new StoreConfig();
				config.setModel(registerProxyClasses());
				config.setAllowCreate(true);
				config.setTransactional(true);				
				
				store = new EntityStore(env, "SpindlesStore", config);
			}
		});		

	}
	
	private EntityModel registerProxyClasses(){
		EntityModel entityModel = new AnnotationModel();
		entityModel.registerClass(BigDecimalProxy.class);
		
		return entityModel;
	}
	
	private void initEnv(){		
		runCmd(new DBCommand(){
			public void execute() throws DatabaseException {
				EnvironmentConfig envConfig = new EnvironmentConfig();
				envConfig.setAllowCreate(true);
				envConfig.setTransactional(true);
				envConfig.setTxnTimeout(0);
				envConfig.setLockTimeout(0);
				
				env = new Environment(Config.getDBHome(), envConfig);
			}
		});		
	}
	
	public static void close(EntityCursor ec){
		try {
			if(ec != null){
				ec.close();
			}			
		} catch (DatabaseException e) {
			throw new ApplicationException(e);
		}
	}
	
	public static void abort(Transaction txn){
		try {
			if(txn != null){
				txn.abort();
			}			
		} catch (DatabaseException e) {
			throw new ApplicationException(e);
		}
	}
	
	public static void close(){		
		runCmd(new DBCommand(){
			public void execute() throws DatabaseException {
				db.store.sync();
				db.store.close();
				if (db.env != null) {
					db.env.sync();
					db.env.cleanLog(); // Clean the log before closing
					db.env.close();	
					
					db.log.info("Closed database...");
				}			
			}
		});		
	}
	
	

}
